"""
    Contains SQLObject inheritor.
    Added utility method for using in mnemoply project.
"""

import sqlobject.col
from sqlobject import SQLObject
from sqlobject import events

class ModelObject(SQLObject):
    def __classinit__(cls, new_attrs):
        """
            Added python layer-triggers. We call them `events`.
            Also make assertion for title and model_title attribute.
        """

        # Register handle_*
        handlers = (
            ('handle_delete', events.RowDestroySignal),
            ('handle_create', events.RowCreateSignal),
            ('handle_created', events.RowCreatedSignal),
            ('handle_update', events.RowUpdateSignal),
        )
        for name, signal in handlers:
            if hasattr(cls, name):
                events.listen(getattr(cls, name), cls, signal, weak=False)

        SQLObject.__classinit__(cls, new_attrs)

        if cls.__name__.count('ModelObject') != 1:
            assert hasattr(cls, 'title')
            assert hasattr(cls, 'model_title')

    @classmethod
    def getModelName(cls):
        return cls.__name__

    def findReferences(self, include_delete_cascade=False):
        """
            Find first level references.
            We get references from foreign keys.
            Returns a list of instances which have a foreign key for `self`.
        """
        import mnemoply.model as model

        references = []
        tables = [getattr(model, name) for name in model.soClasses]
        for cls in tables:
            our_cols = []
            for col in cls.sqlmeta.columnList:
                if col.__class__ == sqlobject.col.SOForeignKey:
                    if not include_delete_cascade and col.cascade is True:
                        continue
                    if col.foreignKey == self.__class__.__name__:
                        our_cols.append(col)

            if len(our_cols) > 0:
                for col in our_cols:
                    deps = list(cls.select(getattr(cls.q, col.name) == self.id))
                    for dep in deps:
                        references.append(dep)
        return references

    def findReferencesCascade(self, include_delete_cascade=False):
        """
            Make a topological sort for all levels in `self` references.
            Returns all references for `self`.
        """

        references = [set(self.findReferences(include_delete_cascade))]

        for level in references:
            if level == set([]):
                break
            dep_list = []
            for dep in level:
                dep_list.extend(dep.findReferences(include_delete_cascade))
            references.append(set(dep_list))

        references.reverse()
        for level in references:
            for another_level in references:
                if level is not another_level:
                    another_level.difference_update(level)
                    #Remove all elements of another set from this set.

        references_list = []
        for level in references:
            references_list.extend(level)

        return references_list

    def destroyCascade(self):
        """
            Destroy all refences for `self`.
        """

        references = self.findReferencesCascade()
        for dep in references:
            dep.destroySelf()

        self.destroySelf()

    @classmethod
    def get_by_null_id(self, id):
        """
            Returns object by id.
            If id is null returns None.
        """

        if id is None or id == "":
            return None
        return self.get(int(id))

    def cloneAndUpdateClone(self, kw):
        """
            Create clone of instance and update them from kw.
            Destroy self and return new object with same id.
        """

        d = self.sqlmeta.asDict()
        d.update(kw)
        self.destroySelf()
        return self.__class__(**d)

    @classmethod
    def addOwnerToData(cls, data, owner):
        """
            Add information about owner to data dictionary.
            Actually, for ModelObject this method doesn't change anything.
        """
        return data

class ModelObjectWithOwner(ModelObject):
    """
        ModelOblect inheritor with owner field.
    """

    def __classinit__(cls, new_attrs):
        ModelObject.__classinit__(cls, new_attrs)

        if cls.__name__ != 'ModelObjectWithOwner':
            assert hasattr(cls, 'ownerID')

    @classmethod
    def addOwnerToData(cls, data, owner):
        """
            Add information about owner to data dictionary.
        """

        if owner:
            data['ownerID'] = owner.id
        else:
            data['ownerID'] = None
        return data
